/* ********************************************************************************
 *      Copyright:  (C) 2025 ZYT
 *                  All rights reserved.
 *    
 *       Filename:  main.c
 *    Description:  This file 
 *                        
 *        Version:  1.0.0(06/04/25)
 *         Author:  Zhang yuting <zhangyuting@163.com>
 *      ChangeLog:  1, Release initial version on "06/04/25 16:09:45"
 *                          
 *********************************************************************************/
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <signal.h>
#include <sqlite3.h>

#include "sqlite.h"
#include "socket.h"
#include "logger.h"
#include "pack.h"

#define MAX_EVENT 1024
static inline void print_usage(char *progname);
void program_stop(int signum);

int g_program_stop = 0;


int main(int argc, char **argv)
{
	int                  sockfd;
	int                  serv_port;
	int                  clifd;

	int                  i;
	char                 opt;
	int                  daemon_run = 0;
	char                 buffer[256];

	int                  epollfd;
	struct epoll_event   event;
	struct epoll_event   event_array[MAX_EVENT];
	int                  events;
	
	sqlite3             *sql_db;
	char                 sn[32] = "RPI_00001";
	int                  rv;

	packet_t			 pack = {0};
	parse_proc_t         parse_proc = unpack_data;

	struct option        opt_long[] = 
	{
		{"serv_port", required_argument, NULL, 'p'},
		{"daemon", no_argument, NULL, 'b'},
		{"help", no_argument, NULL, 'h'},
		{"Pack", required_argument, NULL, 'P'},
		{NULL, 0, NULL, 0}
	};

	if( (opt = getopt_long(argc, argv, "p:bh:P", opt_long, NULL)) != -1 )
	{

		switch(opt)
		{
			case 'p':
				serv_port = atoi(optarg);
				break;
			case 'b':
				daemon_run = 1;
				break;
			case 'h':
				print_usage(argv[0]);
				break;
			case 'P':
				if( strcmp(optarg, "json") == 0 )
				{
					parse_proc = unpack_json;
				}
				else if( strcmp(optarg, "tlv") == 0 )
				{
					parse_proc = unpack_tlv;
				}
				else
				{
					print_usage(argv[0]);
				}
				break;
			default:
				break;
		}
	}

	if(!serv_port)
	{
		print_usage(argv[0]);
		return -1;
	}

	/* 初始化日志 */
	rv = log_open("running.log", LOG_LEVEL_DEBUG, 10, 0);
	if( rv < 0)
	{
		fprintf(stderr, "initial logger system failure\n");
		return -2;
	}

	/* 后台运行 */
	if( daemon_run )
	{
		daemon(0, 0);
	}

	signal(SIGINT, program_stop);
	signal(SIGTERM, program_stop);

	/* 初始化数据库 */
	sql_init(&sql_db, sn);

	/* 初始化socket */
	if((sockfd = socket_server_init(serv_port)) < 0 )
	{
		log_error("socket init failure: %s\n", strerror(errno));
		return -2;
	}

	/* 创建epoll实例*/
	if( (epollfd = epoll_create(MAX_EVENT)) < 0 )
	{
		log_error("create epollfd failure\n");
		return -3;
	}
	log_info("create epollfd successfully\n");

	/* 配置并添加服务器socket到epoll监控 */
	event.events = EPOLLIN;
	event.data.fd = sockfd;

	if( epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) < 0 )
	{
		log_error("add sockfd to event failure\n");

		return -4;
	}
	log_info("add sockfd to event successfully\n");

	/* 主事件循环 */
	while( !g_program_stop )
	{
		/* 等待事件（无限阻塞到事件发生）*/
		log_info("wait epoll...\n");

		events = epoll_wait(epollfd, event_array, MAX_EVENT, -1);

		if(events < 0)
		{
			log_error("epoll_wait failure\n");
			break;
		}
		else if(0 == events)
		{
			log_error("epoll_wait timeout\n");
			continue;
		}

		/* 处理所有就绪事件 */
		for(i = 0; i < events; i++)
		{
			/* 错误、挂断事件处理 */
			if( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) )
			{
				log_error("epoll_wait fd failure\n");

				epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
				close(event_array[i].data.fd);
			}

			/* 处理服务器socket事件 （新连接到达）*/
			if(event_array[i].data.fd == sockfd)
			{
				log_info("got sockfd action\n");

				if( (clifd = accept(sockfd, (struct sockaddr *)NULL, NULL)) < 0 )
				{
					log_error("accept clifd failure: %s\n", strerror(errno));
					continue;
				}
				log_info("accept clifd successfully\n");

				/* 添加客户端socket到epoll监控 */
				event.data.fd = clifd;
				event.events = EPOLLIN;

				if( epoll_ctl(epollfd, EPOLL_CTL_ADD, clifd, &event) < 0 )
				{
					log_error("add clifd to event failure: %s\n", strerror(errno));

					close(event_array[i].data.fd);
					continue;
				}
				log_info("add clifd to event successfully\n");
			}


			/* 处理客户端socket事件（数据到达） */
			else
			{
				log_info("got clifd action\n");

				memset(&buffer, 0, sizeof(buffer));

				ssize_t nbytes = read(event_array[i].data.fd, buffer, sizeof(buffer));
				if( nbytes <= 0 )
				{
					log_error("client got disconnect or read data from clifd failure\n");

					epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);

					close(event_array[i].data.fd);
					//free(pack);
					continue;
				}
				buffer[nbytes] = '\0';  

				log_info("start to parser the data\n");

				if ( parse_proc((uint8_t *)buffer, &pack) < 0 )
				{
					/* 将收到的数据解包 */
					log_error("unpack_data failure");
					return -5;
				}

				dbg_printf("got data:%s\n",buffer);
				log_info("got data:%s\n",buffer);
				dbg_printf("start insert sn: %s, time: %s, temp: %f\n", 
						pack.dev_sn, pack.dev_time, pack.dev_temp);

				/* 将解包数据插入数据库 */
				if( sql_insert(sql_db, &pack) < 0 )
				{
					log_error("insert data to %s failure\n", pack.dev_sn);
				}
			}
		}
	}
	close(sockfd);
	sql_close(&sql_db);
	log_close();
}

static inline void print_usage(char *progname)
{
	printf("%s usage:\n", progname);
	printf("-p(--port): specify a port to listen.\n");
	printf("-b(--daemon): set program running in the backgrund.\n");
	printf("-h(--help): print this help information.\n");
	printf("example: %s -p 12345 -b\n", progname);
}

void program_stop(int signum)
{
	g_program_stop = 1;
}
